/*
 * Copyright (C) 2003, Velocityme Partnership
 * TaskStateMachineSessionBean.java
 *
 * Created on 07 May 2003, 08:17
 */

package com.velocityme.session;

import com.velocityme.entity.ChangeDeltaItemTypeBean;
import com.velocityme.entity.ChangeDeltaTypeBean;
import com.velocityme.entity.PermissionBean;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;
import com.velocityme.interfaces.*;
import com.velocityme.utility.InvalidKeyException;
import com.velocityme.utility.PermissionDeniedException;
import com.velocityme.valueobjects.*;
import java.rmi.RemoteException;

/**
 *
 * @author  Robert Crida Work
 * @ejb.bean
 *           type="Stateless"
 *           cmp-version="2.x"
 *           name="TaskStateMachineSession"
 *           jndi-name="ejb/TaskStateMachineSession"
 *           view-type="local"
 *           transaction-type="Container"
 * @ejb.transaction type="Required"
 *
 * @ejb.util generate="physical"
 */
public class TaskStateMachineSessionBean implements SessionBean {
    
    private SessionContext m_context;
    
    private SearchSessionLocal m_searchSessionLocal;
    
    private TransitionValue m_defaultTransitionValue;

    private PermissionLocal m_permissionTaskTypeCreate;
    private PermissionLocal m_permissionTaskTypeEdit;
    private PermissionLocal m_permissionNodeView;

    /**
     * If the user has permission to create a new TaskStateMachine in the 
     * specified parentnode then go ahead and create it and log an appropriate 
     * ChangeDelta.
     *
     * @param p_key User's security key
     * @param p_parentNode The node which will contain the new TaskStateMachine
     * @param p_taskStateMachineValue The value details for the TasktateMachine part of the TaskStateMachine
     *
     * @ejb.interface-method view-type="local"
     **/
    public com.velocityme.interfaces.TaskStateMachineLocal createTaskStateMachine(com.velocityme.interfaces.KeySessionLocal p_keyLocal,
                                                                                  com.velocityme.interfaces.NodeLocal p_parentNodeLocal,
                                                                                  com.velocityme.valueobjects.TaskStateMachineValue p_taskStateMachineValue)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_parentNodeLocal).contains(m_permissionTaskTypeCreate)) {
                p_taskStateMachineValue.getNodeValue().setType("TaskStateMachine");
                p_taskStateMachineValue.getNodeValue().setFinalType("TaskStateMachine");
                DBObjectLocal dBObjectLocal = DBObjectUtil.getLocalHome().create();
                NodeValue nodeValue = p_taskStateMachineValue.getNodeValue();
                NodeLocal nodeLocal = NodeUtil.getLocalHome().create(dBObjectLocal, nodeValue, p_parentNodeLocal);
                nodeValue.setNodeId(nodeLocal.getNodeId());
                Collection defaultTransitionValues = new ArrayList();
                defaultTransitionValues.add(m_defaultTransitionValue);
                p_taskStateMachineValue.setAddedTransitionValues(defaultTransitionValues);
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.CREATE, p_keyLocal.getUserLocal(), dBObjectLocal);
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NAME, nodeValue.getName());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.DESCRIPTION, nodeValue.getDescription());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.PARENT_NODE, p_parentNodeLocal.getPathName());
                calcInterestedRoleChanges(p_taskStateMachineValue, null);
                manageRoleValues(p_taskStateMachineValue, changeDeltaLocal);
                manageTransitionValues(p_taskStateMachineValue, changeDeltaLocal);
                TaskStateMachineLocal taskStateMachineLocal = TaskStateMachineUtil.getLocalHome().create(nodeLocal, p_taskStateMachineValue);
                return taskStateMachineLocal;
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
        catch(RemoteException e) {
            throw new EJBException(e);
        }
        catch(RemoveException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    public void calcInterestedRoleChanges(com.velocityme.valueobjects.TaskStateMachineValue p_taskStateMachineValue,
                                          java.util.Collection p_currentInterestedRoleLocals) {
        p_taskStateMachineValue.cleanInterestedRoleValue();
        RoleValue[] roles = p_taskStateMachineValue.getInterestedRoleValues();
        Collection newRoleValues = new ArrayList();
        for (int i = 0; i < roles.length; i++)
            newRoleValues.add(roles[i]);
        Collection addedRoleValues = new ArrayList(newRoleValues);
        Collection originalInterestedRoleValues = new ArrayList();
        if (p_currentInterestedRoleLocals != null) {
            Iterator it = p_currentInterestedRoleLocals.iterator();
            while(it.hasNext()) {
                RoleLocal roleLocal = (RoleLocal)it.next();
                originalInterestedRoleValues.add(roleLocal.getRoleValue());
            }
        }
        addedRoleValues.removeAll(originalInterestedRoleValues);
        Collection removedRoleValues = new ArrayList(originalInterestedRoleValues);
        removedRoleValues.removeAll(newRoleValues);
        p_taskStateMachineValue.setAddedInterestedRoleValues(addedRoleValues);
        p_taskStateMachineValue.setRemovedInterestedRoleValues(removedRoleValues);
    }
    
    /**
     * Determine how the transitions have changed and set up the value object
     * appropriately so that it performs additions, removals and updates when
     * set.
     *
     * @param p_taskStateMachineValue Contains new values and is updated to reflect changes
     * @param p_transitionLocals The current details in the entity bean
     *
     * @ejb.interface-method view-type="local"
     **/
    public void calcTransitionChanges(com.velocityme.valueobjects.TaskStateMachineValue p_taskStateMachineValue,
                                      java.util.Collection p_currentTransitionLocals) {
        Collection currentTransitionValues = new ArrayList();
        if (p_currentTransitionLocals != null) {
            Iterator it = p_currentTransitionLocals.iterator();
            while(it.hasNext()) {
                TransitionLocal transitionLocal = (TransitionLocal)it.next();
                currentTransitionValues.add(transitionLocal.getTransitionValue());
            }
        }
        p_taskStateMachineValue.cleanTransitionValue();
        TransitionValue[] transitionValues = p_taskStateMachineValue.getTransitionValues();
        Collection newTransitionValues = new ArrayList(transitionValues.length);
        for (int i = 0; i < transitionValues.length; i++) {
            newTransitionValues.add(transitionValues[i]);
        }
        Iterator i = currentTransitionValues.iterator();
        while(i.hasNext()) {
            TransitionValue originalTransition = (TransitionValue)i.next();
            Iterator j = newTransitionValues.iterator();
            while(j.hasNext()) {
                TransitionValue newTransition = (TransitionValue)j.next();
                if (!newTransition.transitionIdHasBeenSet()) {
                    // new transition so all roles are new!
                    Collection newRoles = new ArrayList();
                    RoleValue[] newRoleValues = newTransition.getResponsibleRoleValues();
                    for (int k = 0; k < newRoleValues.length; k++)
                        newRoles.add(newRoleValues[k]);
                    newTransition.setAddedResponsibleRoleValues(newRoles);
                }
                if (originalTransition.getPrimaryKey().equals(newTransition.getPrimaryKey())) {
                    // calculate the changes to the responsible role for this transition
                    Collection newRoles = new ArrayList();
                    RoleValue[] newRoleValues = newTransition.getResponsibleRoleValues();
                    for (int k = 0; k < newRoleValues.length; k++)
                        newRoles.add(newRoleValues[k]);
                    Collection originalRoles = new ArrayList();
                    RoleValue[] roles = originalTransition.getResponsibleRoleValues();
                    for (int k = 0; k < roles.length; k++)
                        originalRoles.add(roles[k]);
                    Collection addedResponsibleRoleValues = new ArrayList(newRoles);
                    addedResponsibleRoleValues.removeAll(originalRoles);
                    Collection removedResponsibleRoleValues = new ArrayList(originalRoles);
                    removedResponsibleRoleValues.removeAll(newRoles);
                    newTransition.setAddedResponsibleRoleValues(addedResponsibleRoleValues);
                    newTransition.setRemovedResponsibleRoleValues(removedResponsibleRoleValues);
                    // now compare the transition to the original one
                    if (!originalTransition.isIdentical(newTransition) || newTransition.getAddedResponsibleRoleValues().size() != 0 || newTransition.getRemovedResponsibleRoleValues().size() != 0) {
                        i.remove();
                        j.remove();
                        p_taskStateMachineValue.updateTransitionValue(newTransition);
                    }
                }
            }
        }
        Collection addedTransitionValues = new ArrayList(newTransitionValues);
        addedTransitionValues.removeAll(currentTransitionValues);
        Collection removedTransitionValues = new ArrayList(currentTransitionValues);
        removedTransitionValues.removeAll(newTransitionValues);
        p_taskStateMachineValue.setAddedTransitionValues(addedTransitionValues);
        p_taskStateMachineValue.setRemovedTransitionValues(removedTransitionValues);
    }
    
    private void manageTransitionValues(TaskStateMachineValue p_taskStateMachineValue,
                                        ChangeDeltaLocal p_changeDeltaLocal) throws NamingException, CreateException, FinderException, RemoteException, RemoveException {
        Iterator i = p_taskStateMachineValue.getAddedTransitionValues().iterator();
        while(i.hasNext()) {
            TransitionValue transitionValue = (TransitionValue)i.next();
            TransitionLocal transitionLocal = TransitionUtil.getLocalHome().create(transitionValue);
            transitionValue.setTransitionId(transitionLocal.getTransitionId());
            // get value from local object because then you don't have to worry about added, updated and removed responsbilities
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.ADD_TRANSITION, new TransitionValueToString(transitionLocal.getTransitionValue()).toString());
        }
        i = p_taskStateMachineValue.getUpdatedTransitionValues().iterator();
        while(i.hasNext()) {
            TransitionValue transitionValue = (TransitionValue)i.next();
            TransitionLocal transitionLocal = TransitionUtil.getLocalHome().findByPrimaryKey(transitionValue.getPrimaryKey());
            StringBuffer strBuffer = new StringBuffer();
            strBuffer.append("{");
            strBuffer.append(new TransitionValueToString(transitionLocal.getTransitionValue()).toString());
            transitionLocal.setTransitionValue(transitionValue);
            strBuffer.append("} -> {");
            strBuffer.append(new TransitionValueToString(transitionLocal.getTransitionValue()).toString());
            strBuffer.append("}");
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.UPDATE_TRANSITION, strBuffer.toString());
        }
        i = p_taskStateMachineValue.getRemovedTransitionValues().iterator();
        while(i.hasNext()) {
            TransitionValue transitionValue = (TransitionValue)i.next();
            TransitionLocal transitionLocal = TransitionUtil.getLocalHome().findByPrimaryKey(transitionValue.getPrimaryKey());
            // get value from local object because then you don't have to worry about added, updated and removed responsbilities
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.REMOVE_TRANSITION, new TransitionValueToString(transitionLocal.getTransitionValue()).toString());
            TransitionUtil.getLocalHome().remove(transitionLocal.getPrimaryKey());
        }
        // clear the list so that the transition won't be removed again when setting the task type value object
        p_taskStateMachineValue.setRemovedTransitionValues(new ArrayList());
    }
    
    private void manageRoleValues(TaskStateMachineValue p_taskStateMachineValue,
                                  ChangeDeltaLocal p_changeDeltaLocal) throws NamingException, CreateException, FinderException, RemoteException, RemoveException {
        Iterator i = p_taskStateMachineValue.getAddedInterestedRoleValues().iterator();
        while(i.hasNext()) {
            RoleValue roleValue = (RoleValue)i.next();
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.ADD_INTERESTED_ROLE, new RoleValueToString(roleValue).toString());
        }
        i = p_taskStateMachineValue.getRemovedInterestedRoleValues().iterator();
        while(i.hasNext()) {
            RoleValue roleValue = (RoleValue)i.next();
            p_changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.REMOVE_INTERESTED_ROLE, new RoleValueToString(roleValue).toString());
        }
    }
    
    /**
     * If the user has permission to edit the task state machine then do so and 
     * log the changes with an appropriate ChangeDelta.
     *
     * @param p_key User's security key
     * @param p_taskStateMachineLocal The Project object being edited
     * @param p_taskStateMachineValue The value details for the TaskStateMachine part of the TaskStateMachine
     *
     * @ejb.interface-method view-type="local"
     **/
    public void editTaskStateMachine(com.velocityme.interfaces.KeySessionLocal p_keyLocal,
                                     com.velocityme.interfaces.TaskStateMachineLocal p_taskStateMachineLocal,
                                     com.velocityme.valueobjects.TaskStateMachineValue p_taskStateMachineValue)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            NodeLocal nodeLocal = p_taskStateMachineLocal.getNodeLocal();
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, nodeLocal).contains(m_permissionTaskTypeEdit)) {
                NodeValue nodeValue = p_taskStateMachineValue.getNodeValue();
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.EDIT, p_keyLocal.getUserLocal(), nodeLocal.getDBObjectLocal());
                if (!nodeValue.getName().equals(nodeLocal.getName()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NAME, nodeValue.getName());
                if (!nodeValue.getDescription().equals(nodeLocal.getDescription()))
                    changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.DESCRIPTION, nodeValue.getDescription());
                nodeLocal.setNodeValue(nodeValue);
                calcTransitionChanges(p_taskStateMachineValue, p_taskStateMachineLocal.getTransitionsLocal());
                calcInterestedRoleChanges(p_taskStateMachineValue, p_taskStateMachineLocal.getInterestedRolesLocal());
                manageRoleValues(p_taskStateMachineValue, changeDeltaLocal);
                manageTransitionValues(p_taskStateMachineValue, changeDeltaLocal);
                p_taskStateMachineLocal.setTaskStateMachineValue(p_taskStateMachineValue);
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
        catch(RemoteException e) {
            throw new EJBException(e);
        }
        catch(RemoveException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * If the user has permission to access a taskStateMachine then return the local object.
     *
     * @param p_key User's security key
     * @param p_taskStateMachinePK The TaskStateMachine object to lookup
     *
     * @ejb.interface-method view-type="local"
     **/
    public TaskStateMachineLocal getTaskStateMachineLocal(KeySessionLocal p_keyLocal, 
                                                          TaskStateMachinePK p_taskStateMachinePK)
        throws PermissionDeniedException, InvalidKeyException {
        try {
            TaskStateMachineLocal taskStateMachineLocal = TaskStateMachineUtil.getLocalHome().findByPrimaryKey(p_taskStateMachinePK);
            NodeLocal nodeLocal = taskStateMachineLocal.getNodeLocal();
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, nodeLocal).contains(m_permissionNodeView)) {
                return taskStateMachineLocal;
            }
            else
                throw new PermissionDeniedException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Get the full list of task state machine value objects.
     *
     * @param p_key User's security key
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getAllValueObjects(com.velocityme.interfaces.KeySessionLocal p_keyLocal) throws InvalidKeyException {
        Collection values = new java.util.ArrayList();
        try {
            if (p_keyLocal.isValid()) {
                Collection taskStateMachines = TaskStateMachineUtil.getLocalHome().findAll();
                Iterator i = taskStateMachines.iterator();
                while(i.hasNext()) {
                    TaskStateMachineLocal taskStateMachineLocal = (TaskStateMachineLocal)i.next();
                    values.add(new TaskStateMachineValueToString(taskStateMachineLocal.getTaskStateMachineValue()));
                }
            }
            else
                throw new InvalidKeyException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
        return values;
    }
    
    /**
     * Create the Session Bean.
     * @throws CreateException 
     */
    public void ejbCreate() throws CreateException {
        try {
            m_searchSessionLocal = SearchSessionUtil.getLocalHome().create();
            m_defaultTransitionValue = new TransitionValue(new Integer(0), Boolean.FALSE, Boolean.FALSE);
            m_defaultTransitionValue.setStatusValue(StatusUtil.getLocalHome().findByName("Default Status").getStatusValue());
            StateValue startStateValue = StateUtil.getLocalHome().findStartState().getStateValue();
            m_defaultTransitionValue.setSourceStateValue(startStateValue);
            m_defaultTransitionValue.setDestinationStateValue(startStateValue);
            m_defaultTransitionValue.setAddedResponsibleRoleValues(new ArrayList());
            m_permissionTaskTypeCreate = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.TASK_TYPE_CREATE)));
            m_permissionTaskTypeEdit = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.TASK_TYPE_EDIT)));
            m_permissionNodeView = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.NODE_VIEW)));
        }
        catch(NamingException e) {
            throw new CreateException(e.getMessage());
        }
        catch(FinderException e) {
            throw new CreateException(e.getMessage());
        }
    }

    public void ejbActivate() throws java.rmi.RemoteException {
    }
    
    public void ejbPassivate() throws java.rmi.RemoteException {
    }
    
    public void ejbRemove() throws java.rmi.RemoteException {
    }
    
    public void setSessionContext(javax.ejb.SessionContext sessionContext) throws java.rmi.RemoteException {
        m_context = sessionContext;
    }
    
}
